﻿using Grow.Domain.EventBus;
using Grow.EventBus.Cap.DomainEventSend;
using Grow.Module.Service;

namespace Grow.EventBus.Cap.PublisherDomainEvents
{
    public class PublisherDomainEventHandler : IPublisherDomainEventHandler
    {
        private readonly IServiceContext _serviceContext;
        private readonly DomainEventBusOptions _eventBusOptions;
        private readonly IDomainEventSend _domainEventSend;
        public PublisherDomainEventHandler(IServiceContext serviceContext)
        {
            _serviceContext = serviceContext;
            _eventBusOptions = _serviceContext.GetOptions<DomainEventBusOptions>();
            _domainEventSend = _serviceContext.GetRequiredService<IDomainEventSend>();
        }

        public async Task PublisherAsync(PublisherDomainEventDto publisherDomainEventDto, CancellationToken cancellationToken = default)
        {
            var failCount = 1;
            while (!cancellationToken.IsCancellationRequested)
            {
                // 执行真正的消息发送
                if (!await DomainEventSendAsync(publisherDomainEventDto, cancellationToken))
                {
                    failCount++;
                }
                else
                {
                    return;
                }
                if (failCount > _eventBusOptions.RetryFailed)
                {
                    return;
                }
            }
        }

        private async Task<bool> DomainEventSendAsync(PublisherDomainEventDto publisherEventDataDto, CancellationToken cancellationToken)
        {
            try
            {
                if (await LockPublisherEventAsync(publisherEventDataDto.Id, cancellationToken))
                {
                    // 这里可能存在的是消息发送成功,数据库更新失败,那么就可能存在重复发送的情况,这个需要消费方自行冥等处理
                    // 事务已提交,执行消息发送和更新状态
                    await _domainEventSend.SendAsync(publisherEventDataDto, cancellationToken);

                    // 消息发送没问题就更新数据库状态
                    await UpdatePublisherEventAsync(publisherEventDataDto.Id, PublisherDomainEventStatus.Success, cancellationToken);
                }
                return true;
            }
            catch
            {
                try
                {
                    await UpdatePublisherEventAsync(publisherEventDataDto.Id, PublisherDomainEventStatus.Fail, cancellationToken);
                }
                catch
                {
                }
                return false;
            }
        }
        private async Task<bool> LockPublisherEventAsync(Guid Id, CancellationToken cancellationToken)
        {
            var serviceContext = _serviceContext.GetServiceContext();
            var publisherDomainEventService = serviceContext.GetRequiredService<IPublisherDomainEventService>();
            return await publisherDomainEventService.LockPublisherEventAsync(Id, cancellationToken);
        }

        private async Task UpdatePublisherEventAsync(Guid Id, PublisherDomainEventStatus publisherDomainEventStatus, CancellationToken cancellationToken)
        {
            var serviceContext = _serviceContext.GetServiceContext();
            var publisherDomainEventService = serviceContext.GetRequiredService<IPublisherDomainEventService>();
            await publisherDomainEventService.UpdatePublisherEventAsync(Id, publisherDomainEventStatus, cancellationToken);
        }
    }
}
